package it.units.dacuts.utility;

/**
 * Class that provides static methods to simply throw unchecked exceptions with
 * checks on an argument.
 */
public final class Check {

    private Check() {}

    /**
     * Throws an {@code IllegalArgumentException} if the condition is
     * {@code false}, with the given error message.
     * 
     * @param condition the condition to check
     * @param errorMessage the error message that describes the check failure
     * @throws IllegalArgumentException if the condition is {@code false}
     */
    public static void argument(boolean condition, String errorMessage) {
        if (!condition) {
            throw new IllegalArgumentException(errorMessage);
        }
    }

    /**
     * Throws a {@code NullPointerException} if the reference is {@code null},
     * with the given error message.
     * 
     * @param <T> the type of the reference
     * @param reference the object reference to check
     * @param errorMessage the error message that describes the check failure
     * @throws NullPointerException if the reference is {@code null}
     */
    public static <T> void notNull(T reference, String errorMessage) {
        if (reference == null) {
            throw new NullPointerException(errorMessage);
        }
    }

    /**
     * Throws an {@code IllegalStateException} if the condition is
     * {@code false}, with the given error message.
     * 
     * @param condition the condition to check
     * @param errorMessage the error message that describes the check failure
     * @throws IllegalStateException if the condition is {@code false}
     */
    public static void state(boolean condition, String errorMessage) {
        if (!condition) {
            throw new IllegalStateException(errorMessage);
        }
    }

    /**
     * Returns an {@code Index} object that permits index related checks.
     * 
     * @param index the numeric index to be checked
     * @return an {@code Index} object
     */
    public static Index index(int index) {
        return new Index(index);
    }

    /**
     * Class that provides index related checks.
     */
    public static class Index {

        private final int index;

        private Index(int index) {
            this.index = index;
        }

        /**
         * Throws an {@code IndexOutOfBoundsException} if the index is greater
         * than or equal to the given value, with the given error message.
         * 
         * @param value the bound value
         * @param errorMessage the error message that describes the check
         *         failure
         * @throws IndexOutOfBoundsException if the index is greater than or
         *         equal to the given value
         */
        public void lessThan(int value, String errorMessage) {
            if (index >= value) {
                throw new IndexOutOfBoundsException(errorMessage);
            }
        }

        /**
         * Throws an {@code IndexOutOfBoundsException} if the index is greater
         * than the given value, with the given error message.
         * 
         * @param value the bound value
         * @param errorMessage the error message that describes the check
         *         failure
         * @throws IndexOutOfBoundsException if the index is greater than the
         *         given value
         */
        public void lessThanOrEqualTo(int value, String errorMessage) {
            if (index > value) {
                throw new IndexOutOfBoundsException(errorMessage);
            }
        }

        /**
         * Throws an {@code IndexOutOfBoundsException} if the index is less
         * than or equal to the given value, with the given error message.
         * 
         * @param value the bound value
         * @param errorMessage the error message that describes the check
         *         failure
         * @throws IndexOutOfBoundsException if the index is less than or
         *         equal to the given value
         */
        public void greaterThan(int value, String errorMessage) {
            if (index <= value) {
                throw new IndexOutOfBoundsException(errorMessage);
            }
        }

        /**
         * Throws an {@code IndexOutOfBoundsException} if the index is less
         * than the given value, with the given error message.
         * 
         * @param value the bound value
         * @param errorMessage the error message that describes the check
         *         failure
         * @throws IndexOutOfBoundsException if the index is less than the
         *         given value
         */
        public void greaterThanOrEqualTo(int value, String errorMessage) {
            if (index < value) {
                throw new IndexOutOfBoundsException(errorMessage);
            }
        }

        /**
         * Throws an {@code IndexOutOfBoundsException} if the index is less
         * than the lower bound or greater than or equal to the upper bound,
         * with the given error message.
         * 
         * @param lowerBound the lower bound (inclusive)
         * @param upperBound the upper bound (exclusive)
         * @param errorMessage the error message that describes the check
         *         failure
         * @throws IndexOutOfBoundsException if the index is less than the lower
         *         bound or greater than or equal to the upper bound
         */
        public void inBounds(int lowerBound, int upperBound, String errorMessage) {
            if (index < lowerBound || index >= upperBound) {
                throw new IndexOutOfBoundsException(errorMessage);
            }
        }

        /**
         * Throws an {@code IndexOutOfBoundsException} if the index is less
         * than or equal to the lower limit or greater than or equal to the
         * upper limit, with the given error message.
         * 
         * @param lowerLimit the lower limit (exclusive)
         * @param upperLimit the upper limit (exclusive)
         * @param errorMessage the error message that describes the check
         *         failure
         * @throws IndexOutOfBoundsException if the index is less than or equal
         *         to the lower limit or greater than or equal to the upper
         *         limit
         */
        public void between(int lowerLimit, int upperLimit, String errorMessage) {
            if (index <= lowerLimit || index >= upperLimit) {
                throw new IndexOutOfBoundsException(errorMessage);
            }
        }

        /**
         * Throws an {@code IndexOutOfBoundsException} if the index is less
         * than the first value or greater than the last value, with the given
         * error message.
         * 
         * @param firstValue the first value permitted (inclusive)
         * @param upperBound the last value permitted (inclusive)
         * @param errorMessage the error message that describes the check
         *         failure
         * @throws IndexOutOfBoundsException if the index is less than the first
         *         value or greater than the last value
         */
        public void inRange(int firstValue, int lastValue, String errorMessage) {
            if (index < firstValue || index > lastValue) {
                throw new IndexOutOfBoundsException(errorMessage);
            }
        }
    }
}
